package com.leyou.order.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.auth.pojo.UserHolder;
import com.leyou.common.constants.LyConstants;
import com.leyou.common.constants.MQConstants;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.item.client.ItemClient;
import com.leyou.item.entity.Sku;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.dto.OrderStatusEnum;
import com.leyou.order.dto.OrderVO;
import com.leyou.order.entity.Order;
import com.leyou.order.entity.OrderDetail;
import com.leyou.order.entity.OrderLogistics;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderLogisticsMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.utils.PayHelper;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.weaver.ast.Or;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional
public class OrderService extends ServiceImpl<OrderDetailMapper, OrderDetail> {

    @Autowired(required = false)
    private OrderMapper orderMapper;

    @Autowired(required = false)
    private OrderDetailMapper orderDetailMapper;

    @Autowired(required = false)
    private OrderLogisticsMapper orderLogisticsMapper;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private UserClient userClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private PayHelper payHelper;

    @Autowired
    private AmqpTemplate amqpTemplate;


    /**
     * 下单
     */
    @GlobalTransactional
    public Long buildOrder(OrderDTO orderDTO) {

        try {
            //=======第一部分：保存订单信息=========
            //获取当前用户的id
            Long userId = UserHolder.getUserId();
            //创建订单订单对象
            Order order = new Order();
            order.setStatus(OrderStatusEnum.INIT.value());  //订单状态
            order.setUserId(userId);
            order.setInvoiceType(0);  //发票类型
            order.setPaymentType(orderDTO.getPaymentType());// 付款类型
            order.setPostFee(0L);  //邮费
            order.setActualFee(1L); //实付金额写一分钱方便测试

            //得到所有提交的购物车数据
            List<CartDTO> carts = orderDTO.getCarts();
            //将列表类型的CartDTO集合转成键值对的集合
            Map<Long, Integer> cartMap = carts.stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));
            //收集到购物车数据中所有的sku的id
            List<Long> skuids = carts.stream().map(CartDTO::getSkuId).collect(Collectors.toList());
            //根据Sku的id的集合查询出Sku对象的集合
            List<Sku> skus = itemClient.findSkusByIds(skuids);
            //计算订单总金额
            long totalFee = skus.stream().mapToLong(sku -> sku.getPrice() * cartMap.get(sku.getId())).sum();
            //给订单总金额赋值
            order.setTotalFee(totalFee);
            //保存订单
            orderMapper.insert(order);

            //==========第二部分：保存订单详情信息=============
            //初始化订单详情集合对象
            List<OrderDetail> orderDetails = new ArrayList<>();
            //创建存储skuId的集合
            List<String> skuIdList = new ArrayList<>();

            //遍历第一部分获取到的sku对象列表
            skus.forEach(sku -> {
                //初始化一个OrderDetail对象
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setOrderId(order.getOrderId());
                orderDetail.setTitle(sku.getTitle());
                orderDetail.setSkuId(sku.getId());
                orderDetail.setPrice(sku.getPrice());
                orderDetail.setOwnSpec(sku.getOwnSpec());
                orderDetail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
                orderDetail.setNum(cartMap.get(sku.getId()));
                orderDetail.setCreateTime(new Date());
                orderDetail.setUpdateTime(new Date());
                //把OrderDetail对象添加到OrderDetail集合中
                orderDetails.add(orderDetail);

                //把skuId加入 skuIdList集合中
                skuIdList.add(sku.getId().toString());

            });
            //保存订单详情列表
            saveBatch(orderDetails);

            //============第三部分：保存物流信息===========
            //获取用户的物流信息
            AddressDTO addressDTO = userClient.queryAddressById(userId, orderDTO.getAddressId());
            //将AddressDTO转成OrderLogistics
            OrderLogistics orderLogistics = BeanHelper.copyProperties(addressDTO, OrderLogistics.class);
            //设置订单号
            orderLogistics.setOrderId(order.getOrderId());
            //保存订单物流信息
            orderLogisticsMapper.insert(orderLogistics);

            //========第四部分：减库存==========
            itemClient.minusStock(cartMap);

            //============================下单后 购物车清除对于商品======================================
            //=======rabbitMQ ===============
            //定义map集合  存储需要发送的消息(ridesKey, List<skuId>)
            HashMap<String , Object> mqMap = new HashMap<>();
            mqMap.put("redisKey", LyConstants.CART_PRE + userId);
            mqMap.put("skuIdList", skuIdList);

            //把需要同步的数据放入MQ中
            //参数一：exchange交换机, 参数二：routingKey, 参数三：要发送的数据，可以是任何格式
            amqpTemplate.convertAndSend(MQConstants.Exchange.CART_EXCHANGE_NAME,MQConstants.RoutingKey.CART_CLEAR_KEY ,mqMap);

//            for (String skuId : skuIdList) {
//                redisTemplate.opsForHash().delete(LyConstants.CART_PRE + userId, skuId);
//            }

            //返回 订单id
            return order.getOrderId();

        } catch (Exception e) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
    }


    /**
     * 订单查询
     */
    public OrderVO findOrderById(Long id) {

        try {
            //根据订单号查询订单
            Order order = orderMapper.selectById(id);
            //将Order对象转成OrderDTO对象
            OrderVO orderVO = BeanHelper.copyProperties(order,OrderVO.class);

            //根据订单号查询订单详情列表
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(id);
            QueryWrapper wrapper = Wrappers.query(orderDetail);
            List<OrderDetail> orderDetails = orderDetailMapper.selectList(wrapper);

            //把OrderDetail集合赋值给OrderVO的属性
            orderVO.setDetailList(orderDetails);

            //根据订单号查询物流信息
            OrderLogistics orderLogistics = orderLogisticsMapper.selectById(id);
            orderVO.setLogistics(orderLogistics);

            return orderVO;
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
    }


    /**
     * 生成支付链接
     */
    public String getPayUrl(Long id) {
        //得到当前支付链接在redis中存储的key
        String payUrlRedisKey = LyConstants.PAY_URL_PRE+id;
        //判断当前订单的支付链接是否已经生成
        Boolean hasKey = redisTemplate.hasKey(payUrlRedisKey);
        //如果存在
        if(hasKey){
            return redisTemplate.opsForValue().get(payUrlRedisKey);
        }
        //如果不存在，我们可以查询订单
        Order order = orderMapper.selectById(id);
        //判断订单状态是否已经支付
        if(!(order.getStatus()==OrderStatusEnum.INIT.value())){
            throw new LyException(501, "订单已支付，不要重复支付！");
        }
        //生成支付链接
        String payUrl = payHelper.getPayUrl(id, order.getActualFee());
        //将支付链接存储到redis中，有效期2小时
        redisTemplate.opsForValue().set(payUrlRedisKey, payUrl, 2, TimeUnit.HOURS);
        return payUrl;
    }


    /**
     * 此处理器是有微信来访问的，所以一些规则按照微信的来
     * 微信要求我们必须返回xml格式的数据
     * 只要处理器上有@ResponseBody，并且项目中有解析xml的jar包，而且要指定返回xml的数据，就可以返回xml数据了
     */
    public void handlerWxResp(Map<String, String> wxNotifyParams) {
        log.info("【微信回调通知】业务开始！");
        //校验通信标识和业务标识
        payHelper.checkWxResp(wxNotifyParams);
        //获取微信通知中的商户订单号
        Long orderId = Long.valueOf(wxNotifyParams.get("out_trade_no"));
        //获取微信通知中的支付金额
        Long totalFee = Long.valueOf(wxNotifyParams.get("total_fee"));
        //根据微信通知中的商户订单号查询订单
        Order order = orderMapper.selectById(orderId);
        if(order==null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //判断订单状态
        if(order.getStatus()!=OrderStatusEnum.INIT.value()){
            throw new LyException(501, "此订单已支付!");
        }
        //比对金额
        if(!totalFee.equals(order.getActualFee())){
            throw new LyException(501, "支付金额与订单应付金额不一致!");
        }
        //修改订单状态
        Order record = new Order();
        record.setOrderId(orderId);
        record.setStatus(OrderStatusEnum.PAY_UP.value());
        record.setPayTime(new Date());
        int count = orderMapper.updateById(record);
        if(count!=1){
            //此处应该将订单状态更新失败的日志入库，后续有人工修改，保证正常给微信成功通知。
            log.error("【微信回调通知】修改订单状态失败！");
        }
        log.info("【微信回调通知】成功结束！");
    }



    /**
     * 查询支付状态
     */
    public Integer queryOrderStatus(Long id) {
        Order order = orderMapper.selectById(id);
        if(order==null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order.getStatus();
    }


    /**
     * 订单展示
     */
    public PageResult<OrderVO> findOrderListByStatus(Integer status, Integer page, Integer rows) {
        //获取用户id
        Long userId = UserHolder.getUserId();
        //提供分页信息
        IPage<Order> ipage = new Page<>(page, rows);
        //提供封装查询条件对象
        QueryWrapper<Order> wrapper = Wrappers.query();
        //查询条件: 用户id
        wrapper.eq("user_id",userId);
        //判断是否有订单状态
        if (status!=null){
            //查询条件: 订单状态
            wrapper.eq("status",status);
        }
        //执行分页查询
        ipage = orderMapper.selectPage(ipage,wrapper);
        List<Order> orders = ipage.getRecords();
        //判断是否为空
        if (CollectionUtils.isEmpty(orders)){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //将List<Spu>转成List<SpuDTO>
        List<OrderVO> orderVOs = BeanHelper.copyWithCollection(orders,OrderVO.class);

        //遍历OrderVOS集合
        //添加 订单详情 / 物流信息
        for (OrderVO orderVO : orderVOs) {
            //获取订单id
            Long orderId = orderVO.getOrderId();
            //根据订单号查询订单详情列表
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(orderId);
            QueryWrapper<OrderDetail> orderDetailWrapper = Wrappers.query(orderDetail);
            List<OrderDetail> orderDetails = orderDetailMapper.selectList(orderDetailWrapper);
            //把OrderDetail集合赋值给OrderVO的属性
            orderVO.setDetailList(orderDetails);

            //根据订单号查询物流信息
            OrderLogistics orderLogistics = orderLogisticsMapper.selectById(orderId);
            //把物流信息赋值给OrderVO的属性
            orderVO.setLogistics(orderLogistics);
        }

        //得到自定义的分页对象
        PageResult<OrderVO> pageResult = new PageResult<>(ipage.getTotal(),ipage.getPages(),orderVOs);

        return pageResult;
    }

    /**
     * 查询支付过期订单(时间>20分钟)
     */
    public List<Order> findFailureOrder(){
        //获取当前的日期
        Date nowDate = new Date();
        //获取当前时间毫秒值
        long nowDataTime = nowDate.getTime();
        //设置要获取到什么样的时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        //获取当前日期对象 转成String类型
//        String nowDateStr = sdf.format(nowDate);
//        //(now - 24小时)
//        String yesterdayDataStr = sdf.format(new Date(nowDataTime - 86400000));

        //支付过期订单截止时间点    (创建时间 < 支付过期时间点  判定为失效)
        String failureDataStr = sdf.format(new Date(nowDataTime - 1200000));

        //提供封装查询条件对象
        QueryWrapper<Order> wrapper = Wrappers.query();
        //查询条件:
        wrapper.eq("status",1)   //订单状态
                //创建订单时间 < 支付过期时间点
                .apply("UNIX_TIMESTAMP(create_time) <= UNIX_TIMESTAMP('" + failureDataStr + "')");
//                //设置查询时间段 [ now - 24h ]
//                .apply(StringUtils.isNotEmpty(yesterdayDataStr),"date_format (create_time,'%Y-%m-%d') >= date_format('" + yesterdayDataStr + "','%Y-%m-%d')")
//                .apply(StringUtils.isNotEmpty(nowDateStr),"date_format (create_time,'%Y-%m-%d') <= date_format('" + nowDateStr + "','%Y-%m-%d')")

        //执行查询
        List<Order> orders = orderMapper.selectList(wrapper);
        return orders;
    }


    /**
     * 修改失效的订单状态
     *         超时20分钟-- 7
     *         超时2小时 -- 8
     *
     * @param failureOrder
     */
    public void changeStatus(List<Order> failureOrder) {
        //===============================创建订单时间>20min 的订单===========================================================
        if (failureOrder!=null){  // 20min<   < 2h
            //遍历每一个支付过期的订单
            for (Order order : failureOrder) {
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setOrderId(order.getOrderId());
                //创建查询条件
                QueryWrapper<OrderDetail> wrapper = Wrappers.query(orderDetail);
                //查询订单中的sku集合
                List<OrderDetail> orderDetails = orderDetailMapper.selectList(wrapper);
                //遍历sku集合
                for (OrderDetail detail : orderDetails) {
                    //获取当前orderDetail的数量
                    Integer num = detail.getNum();
                    //获取skuId
                    Long skuId = detail.getSkuId();
                    //归还库存
                    itemClient.returnSku(skuId,num);
                }
                order.setStatus(7);
                orderMapper.updateById(order);
            }
        }
//=========================================创建时间>2h的订单   =================================================
        //获取当前的日期
        Date nowDate = new Date();
        //获取当前时间毫秒值
        long nowDataTime = nowDate.getTime();
        //设置要获取到什么样的时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //失效订单截止时间点    (创建时间 < 失效时间点  判定为失效)
        String failureDataStr = sdf.format(new Date(nowDataTime - 7200000));
        //提供封装查询条件对象
        QueryWrapper<Order> wrapper = Wrappers.query();
        //查询条件:
        wrapper.eq("status",7)   //订单状态
                //创建订单时间 < 支付过期时间点
                .apply("UNIX_TIMESTAMP(create_time) <= UNIX_TIMESTAMP('" + failureDataStr + "')");
        //执行查询   [失效订单集合]
        List<Order> orders = orderMapper.selectList(wrapper);
        if (orders!=null){  //>2h
            for (Order order : orders) {
                order.setStatus(8);
                orderMapper.updateById(order);
            }
        }

    }
}
